/*
 * Convertor.java
 *
 */

package com.inibb.app.util;

import hk.gov.ogcio.egis.rm.chinesehandling.CodepointSetUtil;
import hk.gov.ogcio.egis.rm.chinesehandling.ValidationUtil;
import hk.gov.ogcio.egis.rm.chinesehandling.ValidationUtil.Mode;

import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.EnumSet;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;



public class SimpChineseUtil {
    
	private static final Log LOG = LogFactory.getLog(SimpChineseUtil.class);
    
    private static EnumSet<Mode> modeSetSC =  EnumSet.noneOf(ValidationUtil.Mode.class);
    private static EnumSet<Mode> modeSetSCNumeric =  EnumSet.noneOf(ValidationUtil.Mode.class);
    private static EnumSet<Mode> modeSetSCAlphaNumeric =  EnumSet.noneOf(ValidationUtil.Mode.class);
    
    static {
        modeSetSC.add(ValidationUtil.Mode.CHINESE);
        modeSetSC.add(ValidationUtil.Mode.SPACE);
        
        modeSetSCNumeric.add(ValidationUtil.Mode.CHINESE);
        modeSetSCNumeric.add(ValidationUtil.Mode.DIGIT);
        modeSetSCNumeric.add(ValidationUtil.Mode.SPACE);
        modeSetSCNumeric.add(ValidationUtil.Mode.PUNCTUATION);

        modeSetSCAlphaNumeric.add(ValidationUtil.Mode.CHINESE);
        modeSetSCAlphaNumeric.add(ValidationUtil.Mode.ALPHABET);
        modeSetSCAlphaNumeric.add(ValidationUtil.Mode.DIGIT);
        modeSetSCAlphaNumeric.add(ValidationUtil.Mode.SPACE);
        modeSetSCAlphaNumeric.add(ValidationUtil.Mode.PUNCTUATION);
        
        try {
            init();
        } catch (Exception ex) {
            //LOG.warn("Unable to init ChineseUtil: " + ex.getMessage());
        	LOG.error("Unable to init ChineseUtil: ", ex);
        }
    }

	private static SimpChineseUtil cu = null;
	//
	private final static String CONFIG_PATH_PFX = "/com/pccw/oframework/utility/validation";
	private final static String HKSCS_IN_ISO =  CONFIG_PATH_PFX+ "/HKSCSSet.txt";
	private final static String BIG5_IN_ISO = CONFIG_PATH_PFX+ "/Big5Set.txt";
	private final static String HKSCSCP_SET = CONFIG_PATH_PFX+ "/HKSCSCPSet.txt";
	private final static String USER_EXCLLUDE_SET = CONFIG_PATH_PFX+ "/UserExcludeSet.txt";
	private final static String ADDITIONAL_SET_RESOURCE = CONFIG_PATH_PFX+ "/GB2312Set.txt";
	
//	private static final String BIG5_IN_ISO = "ra/config/validation//Big5Set.txt";
//	private static final String HKSCS_IN_ISO = "config/chinese/HKSCSSet.txt";
//	private static final String HKSCSCP_SET = "config/chinese/HKSCSCPSet.txt";
//	private static final String USER_EXCLLUDE_SET = "config/chinese/UserExcludeSet.txt";
//	private static final String ADDITIONAL_SET_RESOURCE = CONFIG_PATH_PFX+ "/AdditionalSet.txt";

	private ValidationUtil chineseValidUtil;

	/** Creates a new instance of Convertor. */
	private SimpChineseUtil() throws Exception {

		InputStream is = IOPUtil.getResourceAsStream(ADDITIONAL_SET_RESOURCE);
		Set<String> set = CodepointSetUtil.loadCodepointSetFromInputStream(is);
		chineseValidUtil = new ValidationUtil();
        /* this block defines what is Traditional Chinese */
		chineseValidUtil.setBaseSet(set);
        
        /* this line makes the extractCPChar() output */
        //chineseValidUtil.setCPSet(set3);
	}

	public synchronized static SimpChineseUtil getInstance() throws Exception {
		if (cu==null)
			init();
		return cu;
	}
	
	public static synchronized void init() throws Exception{
		cu = null;
		cu = new SimpChineseUtil();
	}

    public boolean isSCAlphaNumeric(String data) {
		if (data==null)
			return false;
		else if ("".equals(data))
			return false;
		else
			LOG.trace("isSCAlphaNumeric check=" + SimpChineseUtil.dumpCodepointHex(data));
    	return chineseValidUtil.checkString(data, modeSetSCAlphaNumeric, null, null);
    }

   
	public boolean isSimplifiedChinese(String data) {
		if (data==null)
			return false;
		else if ("".equals(data))
			return false;
		else
			LOG.trace("isSimplifiedChinese check=" + SimpChineseUtil.dumpCodepointHex(data));
		return chineseValidUtil.checkString(data, modeSetSC, null, null);
	}

    public boolean isSimplifiedChineseNumeric(String data) {
    	if (data==null)
			return false;
		else if ("".equals(data))
			return false;
		else
			LOG.trace("isSimplifiedChineseNumeric check=" + SimpChineseUtil.dumpCodepointHex(data));

		return chineseValidUtil.checkString(data, modeSetSCNumeric, null, null);
    }

    public String convertFullWidthToHalfWidth(String data) {
    	if (data==null)
			return null;        	
        return chineseValidUtil.convertFullWidthToHalfWidth(data);
    }

    public String replaceInvalidChars(String data) {
    	if (data==null)
			return null;        	
    	return chineseValidUtil.replaceInvalidChars(data, modeSetSCAlphaNumeric, null, null);
    }

    protected String extractPUAChars(String data) {
		if (data==null)
			return null;        	
    	return chineseValidUtil.extractPUAChars(data);
    }

    protected String extractCPChars(String data) {
		if (data==null)
			return null;        	
    	return chineseValidUtil.extractCPChars(data);
    }

    public String extractPUACheckChars(String data) {
		if (data==null)
			return null;        	
        
        String puaString = chineseValidUtil.extractPUAChars(data);
        String cpString = chineseValidUtil.extractCPChars(data);

        return unionStrings(puaString, cpString);
        
    }

    public static String[] toStringArray(String pInputString) {



        //combine the PUA and CP characters
        Vector<String> strings = new Vector<String>();
        
        String tmpStr;
        int i = 0;
        while (pInputString!=null && i < pInputString.length()) {
            int codePoint = pInputString.codePointAt(i);
            if (Character.isHighSurrogate(pInputString.charAt(i))) {
                i = i + 2;
            } else {
                i++;
            }
            tmpStr = new String (new int[] {codePoint},0,1);
            strings.add(tmpStr);
        }        
        
        if (strings.size() == 0) {
            return null;
        }

        return strings.toArray(new String[] {});
    }
    
    public static String unionStrings(String data1, String data2) {

        String pInputString = "";
        if (data1 != null) {
            pInputString = pInputString + data1;
        }
        if (data2 != null)
            pInputString = pInputString + data2;


        //combine the PUA and CP characters
        Set<String> foundSet = new TreeSet<String>();
        String tmpStr;
        int i = 0;
        while (pInputString!=null && i < pInputString.length()) {
            int codePoint = pInputString.codePointAt(i);
            if (Character.isHighSurrogate(pInputString.charAt(i))) {
                i = i + 2;
            } else {
                i++;
            }
            tmpStr = new String (new int[] {codePoint},0,1);
            if (!foundSet.contains(tmpStr)) {
                foundSet.add(tmpStr);
            }
        }        
        
        if (foundSet.size() == 0) {
            return null;
        }

        StringBuffer retStrBuf = new StringBuffer(foundSet.size());
        for (String str : foundSet) {
            retStrBuf.append(str);
        }

        return retStrBuf.toString();
        
    }
    
    public void printAllPUAChars() {
        int counts = 0xF8FF - 0xE000 + 1;
        int[] codePoints = new int[counts];
        for (int i=0;i<counts;i++) {
            codePoints[i]=i + 0xE000;
        }
        String testString = new String(codePoints, 0, codePoints.length);
        System.out.println("PUA strings:");
        System.out.println(testString);
    }

    /**
     * Converts String in UTF-8 to Hex String.
     * <p>
     * 
     * @param pInStr
     *            String to convert.
     * @return String converted.
     */
    public static String dumpUtf8Hex(String pInStr)
            throws UnsupportedEncodingException {
        int i = 0;
        byte[] utfBytes = null;

        utfBytes = pInStr.getBytes("UTF8");
        StringBuffer retStrBuf = new StringBuffer(utfBytes.length * 5);

        for (i = 0; i < utfBytes.length; i++) {
            retStrBuf.append(" 0x");
            retStrBuf.append(Integer.toHexString((0xFF & utfBytes[i])));
        }

        return retStrBuf.toString();
    }

    /**
     * Converts String in UTF-16 to Hex String.
     * <p>
     * 
     * @param pInStr
     *            String to convert.
     * @return String converted.
     */
    public static String dumpUtf16Hex(String pInStr) {
        int i = 0;
        char aChar = 0;
        StringBuffer retStrBuf = new StringBuffer(pInStr.length() * 6);

        while (i < pInStr.length()) {
            aChar = pInStr.charAt(i);

            if (Character.isHighSurrogate(aChar)) {
                retStrBuf.append(" {");
            }

            retStrBuf.append("0x");
            retStrBuf.append(Integer.toHexString(aChar));

            if (Character.isLowSurrogate(aChar)) {
                retStrBuf.append('}');
            }

            retStrBuf.append(' ');
            i++;
        }

        return retStrBuf.toString();
    }
    
    public static String dumpCodepointHex(String pInStr) {
        
        if (pInStr==null) return null;
        int i = 0;
        StringBuffer retStrBuf = new StringBuffer(pInStr.length() * 6);

        while (i < pInStr.length()) {
            retStrBuf.append(" 0x");
            retStrBuf.append(Integer.toHexString(pInStr.codePointAt(i)));
            if (Character.isHighSurrogate(pInStr.charAt(i))) {
                i = i + 2;
            } else {
                i++;
            }
        }

        return retStrBuf.toString();
    } 

    public static String dumpByteArrayToHex(byte[] pInByteArr) {
        int i = 0;
        StringBuffer retStrBuf = new StringBuffer(pInByteArr.length * 5);

        for (i = 0; i < pInByteArr.length; i++) {
            retStrBuf.append(" 0x");
            retStrBuf.append(Integer.toHexString((0xFF & pInByteArr[i])));
        }

        return retStrBuf.toString();
    }

    public static byte[] getByteArrFromHexString(String pInStr) {
        int i = 0;
        int j = 0;
        byte[] tmpByteArr = new byte[pInStr.length()];
        StringTokenizer tokenizer = new StringTokenizer(pInStr);
        String strToken;

        while (tokenizer.hasMoreTokens()) {
            strToken = tokenizer.nextToken();

            j = 0;
            while (j < strToken.length()) {
                tmpByteArr[i++] = (byte) Integer.parseInt(strToken.substring(j,
                        j + 2 > strToken.length() ? strToken.length() : j + 2),
                        16);
                j = j + 2;
            }
        }

        byte[] retByteArr = new byte[i];
        System.arraycopy(tmpByteArr, 0, retByteArr, 0, i);
        return retByteArr;
    }

    public ChineseCheckResult checkChinese(String input) {
        boolean result = isSimplifiedChinese(input);
        
        String puaString = null;
        //if (result)
            puaString=this.extractPUACheckChars(input);
        return new ChineseCheckResult(result, input,  puaString);
    }
    
    public static class ChineseCheckResult {
        String inputString;
        String puaCheckString;
        boolean valid;
        boolean havePUA;
        
        protected ChineseCheckResult(boolean result, String inputString, String puaCheckString) {

            this.inputString = inputString;
            this.puaCheckString = puaCheckString;

            valid = result;            
            if (puaCheckString!=null && puaCheckString.length()>0 ) 
                havePUA = true;
            else
                havePUA = false;
        }
        
        public boolean isValid() {
            return valid;
        }

        public boolean havePUA() {
            return havePUA;
        }
        
        public String getInputString() { return inputString;}
        public String getPUAString() { return puaCheckString;}

    }
    
	public static void main(String[] args) throws Exception {
        SimpChineseUtil cu = SimpChineseUtil.getInstance();
        
        /*
         * String num = "1234567890"; String codePointsString = ChineseUtil.dumpCodepointHex(num);
         * System.out.println(codePointsString);
         */
        
        //cu.printAllPUAChars();
        

        //int[] codePoints = new int[] {0x52c1, 0x4F75, 0xE00F, 0xE00F, 0xF308, 0x4F75};
        //int[] codePoints = new int[] {0x244d3, 0x52c1, 0x4F75, 0x4F75, 0xF312};
        //int[] codePoints = new int[] {0x244d3, 0x52c1, 0xE564, 0xE212, 0x4F75, 0x4F75, 0xF312, 0xE212, 0xF57A};
        int[] codePoints = new int[] {0x66fe, 0x8efb, 0x9B43, 0x8BFB, 0x8B80};
        
        String testString = new String(codePoints, 0, codePoints.length);
        System.out.println("testString codepoints=" +SimpChineseUtil.dumpCodepointHex(testString));
        // testString = testString + "1234567890abcdefghijklmnopqrstuvwxyzABCDEFHIJKLMNOPQRSTUVXYZ";

        
        byte[] haha = testString.getBytes("GB2312");
        String convertedString = new String(haha, "GB2312");
        //System.out.println("yo=" + yo);
        System.out.println("converted codepoints=" +SimpChineseUtil.dumpCodepointHex(convertedString));

        System.out.println("testString=" + testString);
        System.out.println("testString codepoints=" +SimpChineseUtil.dumpCodepointHex( testString));
        System.out.println("isSimplifiedChinese=" + cu.isSimplifiedChinese(testString) );
        System.out.println("isSimplifiedChineseNumeric=" + cu.isSimplifiedChineseNumeric(testString) );
        System.out.println("isSCAlphaNumeric=" + cu.isSCAlphaNumeric(testString) );
        System.out.println("extractPUAChars=" +SimpChineseUtil.dumpCodepointHex(cu.extractPUAChars(testString)) );
        System.out.println("extractCPChars=" +SimpChineseUtil.dumpCodepointHex(cu.extractCPChars(testString)) );       
        System.out.println("extractPUACheckChar=" +SimpChineseUtil.dumpCodepointHex(cu.extractPUACheckChars(testString)) );
        
        //System.out.println("replaceInvalidChars=" + cu.replaceInvalidChars(testString) );

        /*
        FileOutputStream fos = null;
        fos = new FileOutputStream("C:/tmp/yo.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos,"BIG5");
        osw.write(testString);
        osw.flush();
        */
		
	}
}